package spynx.util;

import java.util.HashMap;
import java.util.List;

import spynx.desl.Argument;
import spynx.desl.runtime.IDataStorage;
import spynx.desl.runtime.IExecutionContext;
import spynx.desl.runtime.IRuntimeEntity;

public class ArgumentSet implements IRuntimeEntity, IDataStorage {
	private final List<Argument> arguments;

	class ArgumentStorageProxy implements IDataStorage {
		private Object idx;
		private IExecutionContext context;
		
		public ArgumentStorageProxy(IExecutionContext context, Object idx) {
			this.idx = idx;
			this.context = context;
		}

		@Override
		public Object getValue() {
			if (idx instanceof String)
				return getArgument(context, (String)idx, null);
			else 
				return getArgument(context, (Integer)idx, null);
		}

		@Override
		public void setValue(Object value) {
		}

		@Override
		public boolean isReadOnly() {
			return true;
		}

		@Override
		public void setReadOnly(boolean readonly) {
		}
	}
	
	private HashMap<Argument, Object> cached;
	
	public static final ArgumentSet EMPTY = new ArgumentSet(null); 
	
	public ArgumentSet(List<Argument> arguments) {
		this.arguments = arguments;
	}
	
	public List<Argument> getArguments() {
		return this.arguments;
	}
	
	public Argument getArgumentObject(String name) {
		if (arguments != null)
			for (int i = arguments.size() - 1; i>=0; i--)
				if (name.equals(arguments.get(i).getName()))
					return arguments.get(i);
		return null;
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getArgument(IExecutionContext context, String name, T defaultValue) {
		Argument argObj = getArgumentObject(name);
		return (T) (argObj != null ? getArgumentValue(argObj, context) : defaultValue);
	}
	
	@SuppressWarnings("unchecked")
	public <T> T getArgument(IExecutionContext context, int index, T defaultValue) {
		Argument argObj = index >= 0 && index < arguments.size() ? arguments.get(index) : null;
		return (T) (argObj != null ? getArgumentValue(argObj, context) : defaultValue);
	}
	
	private Object getArgumentValue(Argument arg, IExecutionContext context) {
		Object value = null;
		if (this.cached == null)
			this.cached = new HashMap<Argument, Object>();
		if (!cached.containsKey(arg))
			cached.put(arg, value = arg.resolveValue(context));
		else
			value = cached.get(arg);
		return value;
	}

	@Override
	public String getName() {
		return null;
	}

	@Override
	public Object applyOperator(IExecutionContext context, String operator,
			Object... args) {
		Object param = context.resolveValue(args[0]);
		switch(operator.charAt(0)) {
		case '.': //return  getArgument(context.getParent(), (String)param, null);
		case '[': //return getArgument(context.getParent(), (Integer)param, null);
			return new ArgumentStorageProxy(context, param);
		}
		return null;
	}

	public void resolveArgumentValues(IExecutionContext context) {
		for(Argument arg: arguments)
			getArgumentValue(arg, context);
	}

	@Override
	public Object getValue() {
		return this;
	}

	@Override
	public void setValue(Object value) {
			
	}

	@Override
	public boolean isReadOnly() {
		return true;
	}

	@Override
	public void setReadOnly(boolean readonly) {
		
	}
}
